//
//  EzBase64.cpp
//  FFTEST_RTSP_TCP
//
//  Created by 刘跃龙 on 1/8/18.
//  Copyright © 2018 absolutemobile. All rights reserved.
//

#include "STL_Base64.h"
#include <stdio.h>
#include <stdlib.h>

namespace STL
{
    unsigned char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    int _base64Decode(const unsigned char *input, unsigned int input_len, unsigned char *output, unsigned int *output_len)
    {
        static char inalphabet[256], decoder[256];
        int i, bits, c = 0, char_count, errors = 0;
        unsigned int input_idx = 0;
        unsigned int output_idx = 0;

        for (i = (sizeof alphabet) - 1; i >= 0; i--)
        {
            inalphabet[alphabet[i]] = 1;
            decoder[alphabet[i]] = i;
        }

        char_count = 0;
        bits = 0;
        for (input_idx = 0; input_idx < input_len; input_idx++)
        {
            c = input[input_idx];
            if (c == '=')
                break;
            if (c > 255 || !inalphabet[c])
                continue;
            bits += decoder[c];
            char_count++;
            if (char_count == 4)
            {
                output[output_idx++] = (bits >> 16);
                output[output_idx++] = ((bits >> 8) & 0xff);
                output[output_idx++] = (bits & 0xff);
                bits = 0;
                char_count = 0;
            }
            else
            {
                bits <<= 6;
            }
        }

        if (c == '=')
        {
            switch (char_count)
            {
            case 1:
                fprintf(stderr, "base64Decode: encoding incomplete: at least 2 bits missing");
                errors++;
                break;
            case 2:
                output[output_idx++] = (bits >> 10);
                break;
            case 3:
                output[output_idx++] = (bits >> 16);
                output[output_idx++] = ((bits >> 8) & 0xff);
                break;
            }
        }
        else if (input_idx < input_len)
        {
            if (char_count)
            {
                fprintf(stderr, "base64 encoding incomplete: at least %d bits truncated",
                        ((4 - char_count) * 6));
                errors++;
            }
        }

        *output_len = output_idx;
        return errors;
    }

    void _base64Encode(const unsigned char *input, unsigned int input_len, char *output)
    {
        unsigned int char_count;
        unsigned int bits;
        unsigned int input_idx = 0;
        unsigned int output_idx = 0;

        char_count = 0;
        bits = 0;
        for (input_idx = 0; input_idx < input_len; input_idx++)
        {
            bits |= input[input_idx];

            char_count++;
            if (char_count == 3)
            {
                output[output_idx++] = alphabet[(bits >> 18) & 0x3f];
                output[output_idx++] = alphabet[(bits >> 12) & 0x3f];
                output[output_idx++] = alphabet[(bits >> 6) & 0x3f];
                output[output_idx++] = alphabet[bits & 0x3f];
                bits = 0;
                char_count = 0;
            }
            else
            {
                bits <<= 8;
            }
        }

        if (char_count)
        {
            if (char_count == 1)
            {
                bits <<= 8;
            }

            output[output_idx++] = alphabet[(bits >> 18) & 0x3f];
            output[output_idx++] = alphabet[(bits >> 12) & 0x3f];
            if (char_count > 1)
            {
                output[output_idx++] = alphabet[(bits >> 6) & 0x3f];
            }
            else
            {
                output[output_idx++] = '=';
            }
            output[output_idx++] = '=';
        }

        output[output_idx++] = 0;
    }

    std::string base64Decode(std::string data)
    {
        std::string ret = "";
        int padding = 4 - data.size() % 4;
        if (padding == 3)
        {
            printf("Base64Utils: padding error decoding");
            return "";
        }
        else if (padding > 0)
        {
            data.append(padding, '=');
        }

        unsigned int outLength = 0;
        unsigned char *out = (unsigned char *)malloc(data.size() * 3.0f / 4.0f + 1);
        int status = _base64Decode((unsigned char *)data.data(), (unsigned int)data.size(), out, &outLength);
        if (status > 0)
        {
            printf("Base64Utils: error decoding");
            free(out);
            out = NULL;
            outLength = 0;
        }
        else
        {
            ret = std::string((char *)out, outLength);
        }
        free(out);
        return ret;
    }

    std::string base64Encode(std::string data)
    {
        std::string ret = "";
        if (data.empty())
        {
            return ret;
        }
        //    unsigned int outLength = (unsigned int)data.size() * 4 / 3 + ((unsigned int)data.size() % 3 > 0 ? 4 : 0);
        unsigned int outLength = (unsigned int)data.size() % 3 == 0 ? ((unsigned int)data.size() / 3) * 4 : ((unsigned int)data.size() / 3 + 1) * 4; //LYL modify
        char *out = (char *)malloc(outLength + 4);
        _base64Encode((unsigned char *)data.data(), (unsigned int)data.size(), out);
        ret = std::string(out, outLength);
        free(out);
        return ret;
    }
} // namespace STL
